//头文件区域，全局函数区域
#include<stdio.h>
#include<string.h>

//程序1
//指针数组和数组指针的混合使用

void test1()
{
	//指针数组：指针数组是一个数组，每个数组元素存放一个指针变量。
	/*int* p1[5]

		下标		0		1		2		3		4
		元素		int*	int*	int*	int*	int*

		例如：int* p1[5]; 根据关系运算符结合性，[]结合性比* 大, 所以先结合[]再结合* 。
		数组下标的优先级要比取值运算符的优先级高，所以先入为主，p1 被定义为具有 5 个元素的数组。
		那么数组元素的类型呢？是整型吗？显然不是，因为还有一个星号，所以它们应该是指向整型变量的指针。也就是一个数组存放5个指向整型变量的指针
	*/
	const char* array[5] = {"FishC","Five","Star","Good","WoW"};
	/*	
		可能对这里产生疑惑，小甲鱼的代码明明是char* array[5] = {}, 在VS2017版本中使用这种char* 的表达方式会造成程序崩溃
		因为一个字符常量其实是有一个专业名词，字符面量，这个玩意不能修改，字符面量的类型是const，所以必须添加const
		所以VS2017对其进行了控件管理,所以如果你要使用‘字符串类型的指针数组’的话就得加上const
		也可以这么表示:
		char array[5][5] = {"FishC","Five","Star","Good","WoW"}
		char *p = array[0];

		哎，是不是有点新奇，它怎么和二维数组搭起来了。没错：指针数组就是进阶版的二维数组。
	*/

	const char* (*p)[5] = &array;
	/*
		&array是取出整个数组的地址
		这里是一个指向'数组指针'的指针，
		结合性从左到右, 所以 p 先被定义成一个指针变量。
		指向一个具有5个元素的数组，p就指向它，数组类型是 char *，即指向5个char *类型元素的数组的指针(套娃)，
		&array是取出整个数组的地址，而array数组又存放指针;

		假设按照小甲鱼上课所说的char (*p)[5] = &array，<我开始就是那么写的>那就会出现报错.
		因为左边是一个指针指向一个存放char类型的指针,而我们所要的是一个类型相同的函数，右边&array类型是char*型
		所以我们也需要左边是一个char*的数组指针，故才需要char* (*p)[5]啦

		最高层（指针）											*P
			|
			|	指向
			V
		第二层（数组）			下标		0			1			2			3			4
		这个数组又存放指针		元素		char*		char*		char*		char*		char*
			|							|			|			|			|			|
			|	每个指针指向				|			|			|			|			|
			|							|			|			|			|			|
			V							v			V			V			V			V
		第三层							"FishC"		"Five"		"Star"		"Good"		"WoW"

		
		
	*/

	/*
		或许你也会头晕，数组指针又是个啥玩意
		数组指针的含义
		数组指针：数组指针是一个指针，它指向的是一个数组。
		char (*p)[5];

		最高层（指针）							*P
			|
			|	指向
			V
		第二层（数组）			下标		0		1		2		3		4
								元素		char	char	char	char	char
	
		举个例子哈
		char temp[5] = {'a','b','c','d','e'};
		char (*p)[5] = &temp;
		for(int i = 0;i<5;i++)
		{
			printf("%c",(*p)[i]);
		}
		就相当于一个指针指向一个数组(这是指向整个元素)，感觉是不是和之前学的'指针和数组'有点类似？
		char temp[5] = {'a','b','c','d','e'};
		char*p = temp;
		for(int i = 0;i<5;i++)
		{
			printf("%c",*p++);
		}
	
		这两种方法都可以访问数组元素，但是容易观察到第二段代码是指针是指向数组的第一个元素的地址，事实上是一个指向int类型变量的指针（指向变量），而不是指向数组
		数组名 = 数组第一个元素的地址 = 数组的首地址，但是他们的概念不同
		第一段代码才是名正言顺的是一个指向数组的指针
	*/

	/*
	浅浅科普一下，下标法和指针法
	
		在C语言中，下标法和指针法是两种用于访问数组元素的常见方法，它们各有特点，

		下标法
			是通过数组名和下标来访问数组元素。数组名代表数组的首地址，而下标法则是表示元素在数组中的位置偏移量
		(tips:以下代码你可以直接复制，在VS新建一个项目进行验证)
		#include<stdio.h>
		int main()
		{
			int arr[5] = {10,20,30,40,50};
			//使用下标法访问数组的元素
			for(int i = 0;i < 5;i++)
			{
				printf("arr[%d] = %d\n",i,arr[i]);
			}
			return 0;
		}

		指针法
			是一个利用指针变量来访问数组元素，并通过指针的移动来访问数组中的各个元素。由于'数组名'本身就是一个指针常量（指向数组的首元素的地址），所以可以使用指针来操作数组

		#include<stdio.h>
		int main()
		{
			int arr[5] = {10,20,30,40,50};
			int* ptr = arr;//指针prt指向数组arr的首地址

			//使用指针法访问数组元素
			for(int i = 0;i < 5;i++)
			{
				printf("*(prt + %d) = %d \n ",i,*(prt + i));
			}
			return 0;
		}*/

	int i, j;
	for (i = 0; i < 5; i++)
	{
		for (j = 0; (*p)[i][j] != '\0'; j++)//在这里(*p)就是array
		{
			printf("%c",(*p)[i][j]);

		}
		printf("\n");
	}
}


//程序2
//将上面的代码输出结果以竖向排列
void test2()
{
	const char* array[5] = { "FishC","Five","Star","Good","WoW" };
	const char* (*p)[5] = &array;
	int i, j;
	for (i = 0;i < 5; i++)
	{
		for (j = 0;j<5; j++)
		{
			//用F10断点调试法，你就可以知道了
			/*	
			 if（i > strlen( (*p)[j]- 1 ),strlen((*p)[j]获取(*p)[j]所指向的字符的长度，strlen函数返回的长度不包括字符串长度结束符'\0'。
				这里判断 i 时候大于当前字符串长度减 1 ，如果是，说明当前要取的字符串位置超过了该字符串的实际字符范围，就执行break跳出内层循环。


				模拟输出分析：
				- 当 i = 0 时：
				- 对于 "FishC" ，输出 F ；对于 "Five" ，输出 F ；对于 "Star" ，输出 S ；对于 "Good" ，输出 G ；对于 "Wow" ，输出 W 。

				- 当 i = 1 时：
				- 对于 "FishC" ，输出 i ；对于 "Five" ，输出 i ；对于 "Star" ，输出 t ；对于 "Good" ，输出 o ；对于 "Wow" ，输出 o 。

				- 当 i = 2 时：
				- 对于 "FishC" ，输出 s ；对于 "Five" ，输出 v ；对于 "Star" ，输出 a ；对于 "Good" ，输出 o ；对于 "Wow" ，输出 w 。

				- 当 i = 3 时：
				- 对于 "FishC" ，输出 h ；对于 "Five" ，输出 e ；对于 "Star" ，输出 r ；对于 "Good" ，输出 d 。这里 "Wow" 字符串长度为3， i = 3 超出了其长度，所以不再输出 "Wow" 的字符。

				- 当 i = 4 时：
				- 对于 "FishC" ，输出 C 。这里 "Five" 、 "Star" 、 "Good" 、 "Wow" 字符串长度都小于等于4， i = 4 超出了它们的长度，所以不再输出这些字符串的字符。
			*/
			if (i > strlen((*p)[j]) - 1)
			{
				break;
			}
			printf("%c ", (*p)[j][i]);
		}
		printf("\n");
	}


}


//主函数区域
int main()
{
	/*
		索引			含义
		test1		指针数组和数组指针的混合使用
		test2		将上面的代码输出结果以竖向排列
	
	*/
	test2();
	return 0;
}